package com.onpositive.notes.android.tools;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import com.onpositive.notes.parser.BasicNoteParser;
import com.onpositive.notes.parser.Block;

import android.graphics.Color;
import android.text.Editable;
import android.text.Spannable;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.widget.TextView;

public class TextColorProvider implements TextWatcher{

	private TextView owner ;
	private boolean blockTextColoring = false ;
	private BasicNoteParser parser = new BasicNoteParser() ;
	String previousString = "" ;
	
	private HashMap<String,Integer> colorMap ;
	
	public TextColorProvider( TextView owner ) {
		this.owner = owner ;
		init() ;
	}
	private void init(){
		colorMap = new HashMap<String, Integer>() ;
		
		colorMap.put(Block.BLOCK_KIND_UNIT, Color.BLACK );
		
		colorMap.put(Block.BLOCK_KIND_STRING, Color.BLACK );
		
		colorMap.put(Block.BLOCK_KIND_DATE, Color.rgb(0x00, 0x00, 0x80) );
		
		colorMap.put(Block.BLOCK_KIND_DIMENSION, Color.rgb(0x00, 0x80, 0xa0) );

		colorMap.put(Block.BLOCK_KIND_SCALAR, Color.rgb(0x60, 0x60, 0x00) );
		
		colorMap.put(Block.BLOCK_KIND_MARK, Color.rgb(0x00, 0x60, 0x60) );
		
		colorMap.put(Block.BLOCK_KIND_KEYWORD, Color.rgb(0x00, 0x80, 0x00) );
	}

	@Override
	public void afterTextChanged(Editable arg0) {}

	@Override
	public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}

	ArrayList<Block<?>> blocks = new ArrayList<Block<?>>();
	ArrayList<Block<?>> blocksTmp = new ArrayList<Block<?>>() ;
	
	@Override
	public void onTextChanged(CharSequence txt, int start, int before, int after) {
		
		if(blockTextColoring)			
			return ;
		
		String newString = txt.toString();
//		if( newString.trim().equals(previousString.trim()) )
//			return ;
//		
//		previousString = newString ;
		
		long startTime = System.currentTimeMillis() ;
		
		
		int[] bounds = estimateBounds( start, before, after ) ;
		updateBlocks(newString, bounds);
		
		long endTime = System.currentTimeMillis() ;
		System.out.println( "parse: "+ (endTime - startTime) );
		
		
		//final StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD);		
		
		blockTextColoring = true ;
		long startTime1 = System.currentTimeMillis() ;		
		setSpan( owner.getEditableText(), newString );		
		long endTime1 = System.currentTimeMillis() ;
		System.out.println( "set text: " +(endTime1 - startTime1 ));
		blockTextColoring = false ;
		
		blocks.clear() ;
		blocks.addAll(blocksTmp) ;
		
		previousString = newString ;
	}
	
	public void updateBlocks(String newString, int[] bounds)
	{
		final String trim = newString.trim();
		if( previousString.trim().equals(trim)
				&& blocksTmp != null && !blocksTmp.isEmpty() ){
			
			int i0 = previousString.indexOf(trim) ;
			int i1 = newString.indexOf(trim) ;

			if(i1!=i0){
				int dif = i1-i0 ;
				for( Block<?> blk : blocksTmp ){
					int s = blk.getStart() ;
					int e = blk.getEnd() ;
					blk.setStart(s+dif) ;
					blk.setEnd(e+dif) ;
				}
				Block<?> fBlock = blocksTmp.get(0);
				fBlock.setStart(0) ;
			}
			
			i0 = previousString.length() - i0 ;
			i1 = newString.length() - i1 ;
			
			if( i0 != i1 ){
				int dif = i1-i0 ;
				Block<?> lBlock = blocksTmp.get(blocksTmp.size()-1);
				
				int e = lBlock.getEnd() ;
				lBlock.setEnd(e+dif) ;
			}			
			return ;
		}
		
		String deltaString = newString.substring( bounds[0], bounds[1] ) ;		
		ArrayList<Block<?>> newBlocks = parser.extractBlocks( deltaString );
		blocksTmp.clear() ;
		for( int i = 0 ; i < bounds[2] ; i++ )
			blocksTmp.add( blocks.get(i) ) ;
		
		int off = 0 ;
		if( !blocksTmp.isEmpty() )
			off = blocksTmp.get( blocksTmp.size()-1 ).getEnd() ;
		
		for( Block<?> blk : newBlocks ){
			blk.applyOffset(off) ;
			blocksTmp.add( blk ) ;
		}
		
		if( !blocksTmp.isEmpty() )
			off = blocksTmp.get( blocksTmp.size()-1 ).getEnd() ;
		
		if( bounds[3]+1 < blocks.size() ){
			int off0 = blocks.get(bounds[3]+1).getStart();
			off-=off0 ;			
		}
		for( int i = bounds[3]+1 ; i < blocks.size() ; i++ ){			
			Block<?> blk = blocks.get(i);
			blk.applyOffset(off) ;
			blocksTmp.add(blk) ;
		}
	}
	
	
	private int[] estimateBounds(int start, int before, int after)
	{
		if( blocks.isEmpty() )
			return new int[]{start,after,0,0};
		
		int end = start + before ;
		int startBlock = 0, endBlock=blocks.size()-1 ;
		for( int i = 0 ; i < blocks.size() ; i++ ){
			Block<?> blk = blocks.get(i) ;
			
			int s = blk.getStart() ;
			int e = blk.getEnd() ;
			if( start >= s && start <= e )
				startBlock = i ;
			
			if( end >= s && end <= e )
				endBlock = i ;
		}
		
		while( startBlock>0 ){
			
			Block<?> blk = blocks.get( startBlock-1 ) ;
			String kind = blk.getKind();
			if( kind == Block.BLOCK_KIND_SCALAR || kind == Block.BLOCK_KIND_DIMENSION || kind == Block.BLOCK_KIND_DATE ){
				startBlock-- ;
				break ;
			}
			if( kind == Block.BLOCK_KIND_MARK ){
				break ;
			}
			startBlock-- ;
		}
		
		while( endBlock<blocks.size()-1 ){
			
			Block<?> blk = blocks.get( endBlock+1 ) ;
			String kind = blk.getKind();
			if( kind == Block.BLOCK_KIND_SCALAR || kind == Block.BLOCK_KIND_DIMENSION ){
				endBlock++ ;
				break ;
			}
			if( kind == Block.BLOCK_KIND_MARK || kind == Block.BLOCK_KIND_DATE ){
				break ;
			}
			endBlock++ ;
		}
		
		int sStart = blocks.get(startBlock).getStart() ;
		int sEnd = blocks.get(endBlock).getEnd()-before+after ;		
		return new int[]{ sStart, sEnd, startBlock, endBlock };
	}
	public void setSpan( Editable editable, String newString )
	{
		int bStart = 0 ;
		int bEnd = blocksTmp.size() ;
		
		if( blocks != null ){
			
			int i=0 ;
			for(  ; i < blocksTmp.size() && i < blocks.size(); i++ ){
				
				Object obj0 = blocksTmp.get(i).getVal();
				Object obj1 = blocks.get(i).getVal();
				if(! obj0.equals(obj1))
					break ;
			}
			bStart = i ;
			
			i = blocksTmp.size() ;
			int j = blocks.size() ;
			for(  ; i > bStart && j > bStart; i--, j-- ){
				
				Object obj0 = blocksTmp.get(i-1).getVal();
				Object obj1 = blocks.get(j-1).getVal();
				if(! obj0.equals(obj1))
					break ;
			}
			bEnd = i ;			
		}

		for( int i = bStart ; i < bEnd ; i++ ){
			Block<?> blk = blocksTmp.get(i) ;
			int s = blk.getStart() ;//TODO update block bounds
			int e = blk.getEnd() ;
			
			String kind = blk.getKind() ;
			int color = colorMap.get(kind) ;
			ForegroundColorSpan fcs = new ForegroundColorSpan( color );
			editable.setSpan(fcs, s, e, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
			//sb.setSpan(bss, tStart, tEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
		}
	}
	
	public void setInitialString(String previousString) {
		this.previousString = previousString;
	}
	public void setInitialBlocks( Collection<Block<?>> blocksTmp) {
		if( blocksTmp != null )
			this.blocksTmp = new ArrayList<Block<?>>( blocksTmp );
	}

}
